package com.flash.dataU.test.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Random;
import java.util.Scanner;

public class Guess {

    private Random rand = new Random(System.currentTimeMillis());
    private LinkedList<Integer> numPool = new LinkedList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));

    //初始猜测结果
    private Integer[] guessNum = { 1, 2, 3 };

    //三个位置数字是否确定标记
    private Boolean[] certainNum = { false, false, false };

    //用于查询历史反馈结果
    private ArrayList<String> stepFeedback = new ArrayList<String>();

    //用于查询最近几次的猜测结果
    private LinkedList<Integer[]> stepGuess = new LinkedList<Integer[]>();

    //内部路由标记，用于重定向
    private String innerRoutineTag = null;

    //随机生成一个1-9不重复的三位数，用于自测
    private Integer[] selfNum = new Integer[3];

    {
        stepGuess.add(clone());
        LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
        for (int i = 0; i < selfNum.length; i++) {
            selfNum[i] = tmp.remove(rand.nextInt(tmp.size()));
        }
    }

    /**
     * 将当前值复制下来，压入结果栈stepGuess中
     */
    public Integer[] clone(){
        Integer[] ints = new Integer[3];
        for(int i = 0; i < guessNum.length; i++){
            ints[i] = new Integer(guessNum[i]);
        }
        return ints;
    }

    /**
     * 环形移动：1-2 2-3 3-1
     */
    private void circleMove(Integer[] former){
        guessNum[0] = new Integer(former[2]);
        guessNum[1] = new Integer(former[0]);
        guessNum[2] = new Integer(former[1]);
    }

    /**
     * 交换两个位置上的数字
     */
    private void changePos(int a, int b, Integer[] former){
        guessNum[a] = new Integer(former[b]);
        guessNum[b] = new Integer(former[a]);
        guessNum[3-a-b] = new Integer(former[3-a-b]);
    }

    /**
     * 确定一个位置上的数字
     */
    private void makeSureOne(int a, Integer[] former){
        guessNum[a] = new Integer(former[a]);
        certainNum[a] = true;
        numPool.remove(former[a]);
    }

    /**
     * 确定两个位置上的数字,并随机生成最后一位数字
     */
    private void makeSureTwo(int a, int b, Integer[] former){
        certainNum[a] = true;
        guessNum[a] = former[a];
        certainNum[b] = true;
        guessNum[b] = former[b];
        numPool.removeAll(Arrays.asList(former));
        getLast();
    }

    /**
     * 随机生成只需最后一位数字的三位数
     */
    private void getLast(){
        Integer[] former = stepGuess.peek();
        for (int i = 0; i < guessNum.length; i++) {
            if(!certainNum[i]){
                numPool.remove(former[i]);
            }
        }
        peek();
    }

    /**
     * 生成猜测结果guessNum
     */
    private void peek(){
        LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
        for (int i = 0; i < guessNum.length; i++) {
            if(!certainNum[i]){
                System.out.println(tmp.size());
                guessNum[i] = tmp.remove(rand.nextInt(tmp.size()));
            }
        }
    }

    /**
     *  0s0b 说明三个位置上都不可能是已猜的三个数字
     *  目的：重新选择
     *  策略：从数字池中删除已猜的三个数字，从剩下的数字中随机选出三个
     */
    private void resultOne() {
        numPool.removeAll(Arrays.asList(guessNum));
        LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
        for (int i = 0; i < guessNum.length; i++) {
            guessNum[i] = tmp.remove(rand.nextInt(tmp.size()));
        }
    }


    /**
     *  0s1b 说明三个数字只有一个数字在结果中出现，但不确定是哪个数字
     *  目的：1s0b
     *  策略：1.最多通过两次环形移动可以令结果变为1s0b
     *         例：123 第一次环形移动 结果为312 由于三个数字都改变了位置 所以变为1s0b的几率很大
     *             如果仍为0s1b，那么第二次环形移动231，肯定会变为1s0b
     *       2.确定1s的位置
     *         0s1b，1不动，交换2和3，1s0b 1为正确位置
     *                              0s1b 回到起点 2不动，交换1和3 1s0b 2为正确位置
     *                                                         0s1b 3为正确位置
     *       3.将内部路由标记改为1s0b
     *
     */
    private void resultTwo() {
        if(innerRoutineTag.equals("0s1b")){
            if(stepFeedback.size()==1 && stepFeedback.get(0).equals("0s1b")){
                circleMove(stepGuess.peek());
                stepFeedback.clear();
            }else{
                if(stepFeedback.size()==1){
                    changePos(1, 2, stepGuess.peek());
                }else if(stepFeedback.size()==2){
                    if(stepFeedback.get(1).equals("1s0b")){
                        certainNum[0] = true;
                        numPool.removeAll(Arrays.asList(guessNum));
                        innerRoutineTag = "1s0b";
                        resultFive();
                    }else{
                        changePos(0, 2, stepGuess.get(1));
                    }
                }else{
                    if(stepFeedback.get(2).equals("1s0b")){
                        makeSureOne(1, stepGuess.get(2));
                    }else{
                        makeSureOne(2, stepGuess.get(2));
                    }
                    numPool.removeAll(Arrays.asList(stepGuess.get(2)));
                    innerRoutineTag = "1s0b";
                    stepFeedback.add("1s0b");
                    resultFive();
                }
            }
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     *  0s2b 说明三个数字中有两个在正确结果中出现
     *  目的：2s0b 3s
     *  策略：0s2b 1.交换1和2 2s0b 12正确
     *                       1s1b 交换1和3 2s0b 改变1 2s0b 23正确
     *                                               1s0b 12正确
     *                                               1s1b 12正确 3s
     *                                    0s2b 环形移动 改变2 1s0b 12正确
     *                                                      2s0b 13正确
     *                                                      1s1b 12正确 3s
     *                       0s2b 交换1和3 2s0b 13正确
     *                                    0s2b 环形移动 23正确
     *                                    1s1b 改变3 1s1b 1正确 23交换 3正确
     *                                               1s2b 1正确 23交换 3s
     *                                               0s1b 3正确 12交换 2正确
     *                                               0s2b 环形移动 23正确 3s
     *            2.随机生成最后一位 3s
     */
    private void resultThree() {
        if(innerRoutineTag.equals("0s2b")){
            if(stepFeedback.size() == 1){
                changePos(0, 1, stepGuess.peek());
            }else{
                if(stepFeedback.get(1).equals("2s0b")){
                    makeSureTwo(0, 1, stepGuess.peek());
                }else if(stepFeedback.get(1).equals("1s1b")){
                    if(stepFeedback.size()==2){
                        changePos(0, 2, stepGuess.peek());
                    }else if(stepFeedback.size()==3){
                        LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
                        tmp.removeAll(Arrays.asList(guessNum));
                        if(stepFeedback.get(2).equals("2s0b")){
                            guessNum[0] = tmp.peek();
                        }else{
                            circleMove(stepGuess.peek());
                            guessNum[1] = tmp.peek();
                        }
                    }else if(stepFeedback.size()==4){
                        if(stepFeedback.get(2).equals("2s0b")){
                            if(stepFeedback.get(3).equals("1s0b")){
                                numPool.remove(guessNum[0]);
                                makeSureTwo(0, 1, stepGuess.get(1));
                                getLast();
                            }else if(stepFeedback.get(3).equals("2s0b")){
                                numPool.remove(guessNum[0]);
                                makeSureTwo(1, 2, guessNum);
                                getLast();
                            }else{
                                guessNum[2] = guessNum[0];
                                certainNum[2] = true;
                                makeSureTwo(0, 1, stepGuess.get(1));
                            }
                        }else{
                            if(stepFeedback.get(3).equals("1s0b")){
                                numPool.remove(guessNum[1]);
                                circleMove(stepGuess.get(1));
                                makeSureTwo(0, 1, guessNum);
                                getLast();
                            }else if(stepFeedback.get(3).equals("2s0b")){
                                numPool.remove(guessNum[1]);
                                makeSureTwo(0, 2, guessNum);
                                getLast();
                            }else{
                                int last = guessNum[1];
                                circleMove(stepGuess.get(1));
                                guessNum[2] = last;
                            }
                        }
                    }else{
                        getLast();
                    }
                }else{
                    if(stepFeedback.size()==2){
                        changePos(0, 2, stepGuess.peek());
                    }else if(stepFeedback.size()==3){
                        if(stepFeedback.get(2).equals("2s0b")){
                            numPool.remove(guessNum[1]);
                            makeSureTwo(0, 2, stepGuess.peek());
                        }else if(stepFeedback.get(2).equals("0s2b")){
                            circleMove(stepGuess.peek());
                            makeSureTwo(1, 2, guessNum);
                        }else{
                            LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
                            tmp.removeAll(Arrays.asList(guessNum));
                            guessNum[2] = tmp.peek();
                        }
                    }else if(stepFeedback.size()==4){
                        if(stepFeedback.get(2).equals("1s1b")){
                            if(stepFeedback.get(3).equals("1s1b")){
                                numPool.remove(guessNum[2]);
                                changePos(1, 2, stepGuess.get(1));
                                makeSureTwo(0, 2, guessNum);
                            }else if(stepFeedback.get(3).equals("1s2b")){
                                int last = guessNum[2];
                                changePos(1, 2, stepGuess.get(1));
                                guessNum[1] = last;
                            }else if(stepFeedback.get(3).equals("0s1b")){
                                numPool.remove(guessNum[2]);
                                changePos(0, 1, stepGuess.get(1));
                                makeSureTwo(1, 2, guessNum);
                            }else{
                                int last = guessNum[2];
                                changePos(0, 1, stepGuess.get(1));
                                guessNum[0] = last;
                            }
                        }else{
                            getLast();
                        }
                    }else{
                        getLast();
                    }
                }
            }
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     * 0s3b 说明三个数字都在结果中出现
     * 步骤：3s0b
     * 策略：最多通过两次环形移动即可
     */
    private void resultFour() {
        if(innerRoutineTag.equals("0s3b")){
            circleMove(stepGuess.peek());
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     * 1s0b 说明三个数字中有一个数字为正确数字，目的就是要找到这个数字的正确位置
     * 目的：1s1b或1s2b或2s或3s
     * 策略：1.更改内部路由标记为"0s1b"，找到1s的位置
     *      2.随机生成剩下的两位，1s0b 重复2
     *                          1s1b 更改内部路由标记为1s1b，更新stepFeedback
     *                          1s2b 交换随机随机生成的两位
     *                          2s0b 更改内部路由标记为1s2b，更新stepFeedback
     *                          3s0b 结束
     */
    private void resultFive() {
        if(innerRoutineTag.equals("1s0b")){
            if(stepFeedback.size() == 1){
                innerRoutineTag = "0s1b";
                resultTwo();
            }else{
                if(stepFeedback.get(stepFeedback.size()-1).equals("1s0b")){
                    numPool.removeAll(Arrays.asList(guessNum));
                    peek();
                }else{
                    String lastFeedback = stepFeedback.get(stepFeedback.size()-1);
                    stepFeedback.clear();
                    stepFeedback.add(lastFeedback);
                    int tag = 0;
                    for(int i = 0; i < certainNum.length; i++){
                        if(certainNum[i]){
                            tag = i;
                            break;
                        }
                    }
                    if (lastFeedback.equals("1s1b")) {
                        innerRoutineTag = "1s1b";
                        switch (tag) {
                            case 0:
                                break;
                            case 1:
                                stepGuess.push(clone());
                                stepFeedback.add("0s2b");
                                break;
                            case 2:
                                stepGuess.push(clone());
                                stepGuess.push(clone());
                                stepFeedback.addAll(Arrays.asList("0s2b", "0s2b"));
                                break;
                        }
                        resultSix();

                    }
                    else if (lastFeedback.equals("1s2b")) {
                        switch (tag) {
                            case 0:
                                changePos(1, 2, stepGuess.peek());
                                certainNum[1] = true;
                                certainNum[2] = true;
                                break;
                            case 1:
                                changePos(0, 2, stepGuess.peek());
                                certainNum[0] = true;
                                certainNum[2] = true;
                                break;
                            case 2:
                                changePos(0, 1, stepGuess.peek());
                                certainNum[0] = true;
                                certainNum[1] = true;
                                break;
                        }

                    }
                    else if (lastFeedback.equals("2s0b")) {
                        innerRoutineTag = "2s0b";
                        if (tag == 2) {
                            stepGuess.push(clone());
                            stepFeedback.add("1s1b");
                        }
                        resultEight();

                    }
                }
            }
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     * 1s1b 说明三个数字中一个数字已正确，另一位数字出现，
     * 目的：2s0b 3s0b
     * 策略：2和3交换，2s0b，第一位为正确数字，改变第二位数字，2s0b，第三位为正确数字
     *                                                    1s0b，第二位为正确数字
     *                                                    1s1b，第二位为正确数字，交换23,3s
     *               0s2b，回到起点，交换第一位和第三位，2s0b，第二位为正确数字，改变第一位数字，2s0b，第三位正确
     *                                                                                    1s1b,第一位正确
     *                                                                                    1s0b，第一位正确数字
     *                                                                                    3s0b
     *                                              * 0s2b，第三位为正确数字，回到起点，交换第一位和第二位数字 2s0b 改变第一位数字 1s0b 第一位为正确数字
     *                                                                                                                     2s0b 第二位为正确数字
     *                                                                                                                    *1s1b
     */
    private void resultSix() {
        if(innerRoutineTag.equals("1s1b")){
            if(stepFeedback.size() == 1){
                changePos(1, 2, stepGuess.peek());
            }else{
                if(stepFeedback.get(1).equals("2s0b")){
                    if(stepFeedback.size() == 2){
                        makeSureOne(0, stepGuess.peek());
                        LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
                        tmp.removeAll(Arrays.asList(stepGuess.peek()));
                        guessNum[1] = tmp.peek();
                    }else if(stepFeedback.size() == 3){
                        if(stepFeedback.get(2).equals("2s0b")){
                            makeSureOne(2, stepGuess.peek());
                            numPool.removeAll(Arrays.asList(stepGuess.get(1)));
                            peek();
                        }else if(stepFeedback.get(2).equals("1s0b")){
                            makeSureOne(1, stepGuess.get(1));
                            numPool.removeAll(Arrays.asList(stepGuess.get(1)));
                            peek();
                        }else{
                            guessNum[2] = guessNum[1];
                            certainNum[2] = true;
                            makeSureOne(1, stepGuess.get(1));
                        }
                    }else{
                        getLast();
                    }
                }else{
                    if(stepFeedback.size() == 2){
                        changePos(0, 2, stepGuess.get(1));
                    }else{
                        if(stepFeedback.get(2).equals("2s0b")){
                            if(stepFeedback.size() == 3){
                                makeSureOne(1, stepGuess.peek());
                                LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
                                tmp.removeAll(Arrays.asList(stepGuess.peek()));
                                guessNum[0] = tmp.peek();
                            }else if(stepFeedback.size() == 4){
                                if(stepFeedback.get(3).equals("2s0b")){
                                    makeSureOne(2, stepGuess.get(1));
                                    numPool.remove(guessNum[0]);
                                    numPool.removeAll(Arrays.asList(stepGuess.get(1)));
                                    peek();
                                }else if(stepFeedback.get(3).equals("1s0b")){
                                    makeSureOne(0, stepGuess.get(1));
                                    numPool.removeAll(Arrays.asList(stepGuess.get(1)));
                                    peek();
                                }else{
                                    guessNum[2] = guessNum[0];
                                    certainNum[2] = true;
                                    guessNum[0] = new Integer(stepGuess.get(1)[0]);
                                    certainNum[0] = true;
                                }
                            }else{
                                getLast();
                            }
                        }else{
                            if(stepFeedback.size() == 3){
                                makeSureOne(2, stepGuess.get(2));
                                changePos(0, 1, stepGuess.get(2));
                                LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
                                tmp.removeAll(Arrays.asList(stepGuess.get(2)));
                                guessNum[0] = tmp.peek();
                            }else if(stepFeedback.size() == 4){
                                if(stepFeedback.get(3).equals("1s0b")){
                                    numPool.remove(guessNum[0]);
                                    changePos(0, 1, stepGuess.get(3));
                                    makeSureOne(0, guessNum);
                                    numPool.removeAll(Arrays.asList(stepGuess.get(3)));
                                    peek();
                                }else if(stepFeedback.get(3).equals("2s0b")){
                                    makeSureOne(1, stepGuess.peek());
                                    numPool.removeAll(Arrays.asList(stepGuess.peek()));
                                    peek();
                                }else{
                                    guessNum[1] = guessNum[0];
                                    certainNum[1] = true;
                                    guessNum[0] = new Integer(stepGuess.get(1)[1]);
                                    certainNum[0] = true;
                                }
                            }else{
                                getLast();
                            }
                        }
                    }
                }
            }
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     * 1s2b 说明已有一位数字正确，另外两位数字出现
     * 步骤：3s0b
     * 策略：交换第二位和第三位数字，3s
     *                            3b，回到起点，交换第一位和第三位数字，3s
     *                                                              3b，回到起点，交换第一位和第二位，3s
     */
    private void resultSeven() {
        if(innerRoutineTag.equals("1s2b")){
            if(stepFeedback.size() == 1){
                changePos(1, 2, stepGuess.peek());
            }else if(stepFeedback.size() == 2){
                changePos(0, 2, stepGuess.get(1));
            }else{
                changePos(0, 1, stepGuess.get(2));
            }
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     * 2s0b 说明已有两个数字正确，目的要确定这两个数字的位置
     * 步骤
     * 策略：交换第一位和第二位，0s2b，说明前两位即为正确数字，
     *                        1s1b，说明第三位为正确数字，回到初始位置，改变第一位数字的值，1s0b 第一位为正确数字
     *                                                                                2s0b 第二位为正确数字
     *                                                                             *  1s1b
     *      两位数字确定之后，就不断随机生成剩下的一位，最终3s
     */
    private void resultEight() {
        if(innerRoutineTag.equals("2s0b")){
            if(stepFeedback.size() == 1){
                changePos(0, 1, stepGuess.peek());
            }else{
                if(stepFeedback.get(1).equals("0s2b")){
                    if(stepFeedback.size() == 2){
                        makeSureTwo(0, 1, stepGuess.get(1));
                        peek();
                    }else{
                        getLast();
                    }
                }else{
                    if(stepFeedback.size() == 2){
                        makeSureOne(2, stepGuess.get(1));
                        guessNum[1] = stepGuess.get(1)[1];
                        LinkedList<Integer> tmp = new LinkedList<Integer>(numPool);
                        tmp.removeAll(Arrays.asList(stepGuess.get(1)));
                        guessNum[0] = tmp.peek();
                    }else if(stepFeedback.size() == 3){
                        if(stepFeedback.get(2).equals("1s0b")){
                            makeSureOne(0, stepGuess.get(2));
                            numPool.removeAll(Arrays.asList(stepGuess.get(2)));
                            peek();
                        }else if(stepFeedback.get(2).equals("2s0b")){
                            makeSureOne(1, stepGuess.get(2));
                            numPool.removeAll(Arrays.asList(stepGuess.get(2)));
                            peek();
                        }else{
                            guessNum[1] = guessNum[0];
                            certainNum[1] = true;
                            makeSureOne(0, stepGuess.get(2));

                        }
                    }else{
                        getLast();
                    }
                }
            }
        }else{
            routine(innerRoutineTag);
        }
    }

    /**
     * 路由和重定向到对应方法
     */
    public void routine(String strikeAndBoom) {
        if (strikeAndBoom.equals("0s0b")) {
            resultOne();

        }
        else if (strikeAndBoom.equals("0s1b")) {
            resultTwo();

        }
        else if (strikeAndBoom.equals("0s2b")) {
            resultThree();

        }
        else if (strikeAndBoom.equals("0s3b")) {
            resultFour();

        }
        else if (strikeAndBoom.equals("1s0b")) {
            resultFive();

        }
        else if (strikeAndBoom.equals("1s1b")) {
            resultSix();

        }
        else if (strikeAndBoom.equals("1s2b")) {
            resultSeven();

        }
        else if (strikeAndBoom.equals("2s0b")) {
            resultEight();

        }
        else if (strikeAndBoom.equals("3s0b")) {
            System.out.print("游戏结束！");

        }
        else {
            System.out.println("输入有误！请输入xsxb形式，x取1,2,3");
        }
    }

    /**
     * 用于自测时给出反馈
     */
    private String selfFeedBack(){
        int strike = 0, boom = 0;
        for(int i = 0; i < guessNum.length; i++){
            for(int j = 0; j < selfNum.length; j++){
                if(selfNum[j].equals(guessNum[i])){
                    if(i==j){
                        strike++;
                    }else{
                        boom++;
                    }
                    continue;
                }
            }
        }
        return strike + "s" + boom + "b";
    }

    /**
     * 自测方法
     */
    public void selfTest(){
        System.out.println("随机生成结果：" + Arrays.toString(selfNum) + " 猜测开始！\r\n" + Arrays.toString(guessNum));
        String feedback = null;
        int guessTime = 0;
        do {
            if(guessTime > 15){
                System.exit(0);
            }
            feedback = selfFeedBack();
            System.out.println(feedback);
            if(innerRoutineTag == null && !feedback.equals("0s0b")){
                innerRoutineTag = feedback;
            }
            if(!feedback.equals("0s0b")){
                stepFeedback.add(feedback);
            }
            routine(feedback);
            System.out.println("                      " + numPool);
            stepGuess.push(clone());
            guessTime++;
            System.out.println(Arrays.toString(guessNum));
        } while (!feedback.equals("3s0b"));
        System.out.println(" 一共猜测：" + guessTime + "次。");
    }

    /**
     * 和外界交互
     */
    public void guess() {
        Scanner sc = new Scanner(System.in);
        System.out.println(Arrays.toString(guessNum));
        while (sc.hasNextLine()) {
            String input = sc.nextLine();
            if(innerRoutineTag == null && !input.equals("0s0b")){
                innerRoutineTag = input;
            }
            if(!input.equals("0s0b")){
                stepFeedback.add(input);
            }
            routine(input);
            stepGuess.push(clone());
            System.out.println(Arrays.toString(guessNum));
        }
        sc.close();
    }

    public static void main(String[] args) throws Exception {
		/*Guess guess = new Guess();
		guess.guess();*/
        while(true){
            Guess guess = new Guess();
            guess.selfTest();
            Thread.sleep(100);
        }
    }
}
